#include <srs_kernel_file.hpp>

#ifndef _WIN32
#include <unistd.h>
#include <sys/uio.h>
#endif

#include <fcntl.h>
#include <sstream>
using namespace std;

#include <srs_kernel_log.hpp>
#include <srs_kernel_error.hpp>

SrsFileWriter::SrsFileWriter() {
    fd = 0;
}

SrsFileWriter::~SrsFileWriter() {
    close();
}

int SrsFileWriter::open(const string& p) {
    int ret = ERROR_SUCCESS;
    if (fd != 0) {
        ret = ERROR_SYSTEM_FILE_ALREADY_OPENED;
        srs_error("file %s already opened. ret=%d", path.c_str(), ret);
        return ret;
    }

#ifndef WIN32
    int flags = O_CREAT | O_WRONLY | O_TRUNC;
    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
    if ((fd = ::open(p.c_str(), flags, mode)) < 0) {
#else
    if ((fd = fopen(p.c_str(), "w+")) == nullptr) {
#endif
        ret = ERROR_SYSTEM_FILE_OPENE;
        srs_error("open file %s failed. ret=%d", p.c_str(), ret);
        return ret;
    }

    path = p;
    return ret;
}

int SrsFileWriter::open_append(const string& p) {
    int ret = ERROR_SUCCESS;
    if (fd != 0) {
        ret = ERROR_SYSTEM_FILE_ALREADY_OPENED;
        srs_error("file %s already opened. ret=%d", path.c_str(), ret);
        return ret;
    }

#ifndef WIN32
    int flags = O_APPEND | O_WRONLY;
    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
    if ((fd = ::open(p.c_str(), flags, mode)) < 0) {
#else
    if ((fd = fopen(p.c_str(), "a+")) == nullptr) {
#endif
        ret = ERROR_SYSTEM_FILE_OPENE;
        srs_error("open file %s failed. ret=%d", p.c_str(), ret);
        return ret;
    }

    path = p;
    return ret;
}

void SrsFileWriter::close() {
    int ret = ERROR_SUCCESS;
    if (fd == 0) {
        return;
    }
#ifndef WIN32
    if (::close(fd) < 0) {
#else
    if (fclose(fd) < 0) {
#endif
        ret = ERROR_SYSTEM_FILE_CLOSE;
        srs_error("close file %s failed. ret=%d", path.c_str(), ret);
        return;
    }
    fd = 0;
    return;
}

bool SrsFileWriter::is_open() {
    return fd != 0;
}

void SrsFileWriter::lseek(int64_t offset) {
#ifndef WIN32
    ::lseek(fd, (off_t)offset, SEEK_SET);
#else
    fseek(fd, (off_t)offset, SEEK_SET);
#endif
}

int64_t SrsFileWriter::tellg() {
#ifndef WIN32
    return (int64_t)::lseek(fd, 0, SEEK_CUR);
#else
    return (int64_t)ftell(fd);
#endif
}

int SrsFileWriter::write(void* buf, size_t count, ssize_t* pnwrite) {
    int ret = ERROR_SUCCESS;

    ssize_t nwrite;
    // TODO: FIXME: use st_write.
#ifndef WIN32
    if ((nwrite = ::write(fd, buf, (unsigned int)count)) < 0) {
#else
    if ((nwrite = fwrite(buf, (unsigned int)count, 1, fd)) < 0) {
#endif
        ret = ERROR_SYSTEM_FILE_WRITE;
        srs_error("write to file %s failed. ret=%d", path.c_str(), ret);
        return ret;
    }

    if (pnwrite != NULL) {
        *pnwrite = nwrite;
    }

    return ret;
}

int SrsFileWriter::writev(iovec* iov, int iovcnt, ssize_t* pnwrite) {
    int ret = ERROR_SUCCESS;

    ssize_t nwrite = 0;
    for (int i = 0; i < iovcnt; i++) {
        iovec* piov = iov + i;
        ssize_t this_nwrite = 0;
        if ((ret = write(piov->iov_base, piov->iov_len, &this_nwrite)) != ERROR_SUCCESS) {
            return ret;
        }
        nwrite += this_nwrite;
    }

    if (pnwrite) {
        *pnwrite = nwrite;
    }

    return ret;
}

SrsFileReader::SrsFileReader() {
    fd = 0;
}

SrsFileReader::~SrsFileReader() {
    close();
}

int SrsFileReader::open(const string& p) {
    int ret = ERROR_SUCCESS;
    if (fd != 0) {
        ret = ERROR_SYSTEM_FILE_ALREADY_OPENED;
        srs_error("file %s already opened. ret=%d", path.c_str(), ret);
        return ret;
    }

#ifndef WIN32
    if ((fd = ::open(p.c_str(), O_RDONLY)) < 0) {
#else
    if ((fd = fopen(p.c_str(), "r")) < 0) {
#endif
        ret = ERROR_SYSTEM_FILE_OPENE;
        srs_error("open file %s failed. ret=%d", p.c_str(), ret);
        return ret;
    }

    path = p;
    return ret;
}

void SrsFileReader::close() {
    int ret = ERROR_SUCCESS;
    if (fd == 0) {
        return;
    }

#ifndef WIN32
    if (::close(fd) < 0) {
#else
    if (fclose(fd) < 0) {
#endif
        ret = ERROR_SYSTEM_FILE_CLOSE;
        srs_error("close file %s failed. ret=%d", path.c_str(), ret);
        return;
    }

    fd = 0;
    return;
}

bool SrsFileReader::is_open() {
    return fd != 0;
}

int64_t SrsFileReader::tellg() {
#ifndef WIN32
    return (int64_t)::lseek(fd, 0, SEEK_CUR);
#else
    return (int64_t)ftell(fd);
#endif
}

void SrsFileReader::skip(int64_t size) {
#ifndef WIN32
    ::lseek(fd, (off_t)size, SEEK_CUR);
#else
    fseek(fd, (off_t)size, SEEK_CUR);
#endif
}

int64_t SrsFileReader::lseek(int64_t offset) {
#ifndef WIN32
    return (int64_t)::lseek(fd, (off_t)offset, SEEK_SET);
#else
    return (int64_t)fseek(fd, (off_t)offset, SEEK_SET);
#endif
}

int64_t SrsFileReader::filesize() {
    int64_t cur = tellg();
#ifndef WIN32
    int64_t size = (int64_t)::lseek(fd, 0, SEEK_END);
    ::lseek(fd, (off_t)cur, SEEK_SET);
#else
    fseek(fd, 0, SEEK_END);
    int64_t size = tellg();
    fseek(fd, (off_t)cur, SEEK_SET);
#endif
    return size;
}

int SrsFileReader::read(void* buf, size_t count, ssize_t* pnread) {
    int ret = ERROR_SUCCESS;

    ssize_t nread;
    // TODO: FIXME: use st_read.
#ifndef WIN32
    if ((nread = ::read(fd, buf, (unsigned int)count)) < 0) {
#else
    if ((nread = fread(buf, 1, (unsigned int)count, fd)) < 0) {
#endif
        ret = ERROR_SYSTEM_FILE_READ;
        srs_error("read from file %s failed. ret=%d", path.c_str(), ret);
        return ret;
    }

    if (nread == 0) {
        ret = ERROR_SYSTEM_FILE_EOF;
        return ret;
    }

    if (pnread != NULL) {
        *pnread = nread;
    }

    return ret;
}
